#include "XiolControllerManager.h"

#include <assert.h>
#include <algorithm>
#include "XiolControllerEnumerationTrigger.h"

namespace Xiol
{

ControllerManager::ControllerManager()
	:	mEnumerationTrigger(NULL),
		//mControllers,
		mListeners()
{
	for ( DWORD i=0; i<getMaxNumControllers(); ++i )
		mControllers[i] = NULL;

	// http://msdn.microsoft.com/en-us/library/windows/desktop/ee417001(v=vs.85).aspx
	// "For performance reasons, don't call XInputGetState for an 'empty' user slot every frame. 
	// We recommend that you space out checks for new controllers every few seconds instead."
	mEnumerationTrigger = new TimeBasedEnumerationTrigger( 2000 );
	
	// Use the following Trigger class if you want enumeration to happen only when a device (any device! 
	// not just XInput ones) is connected to or disconnected from the computer
	//mEnumerationTrigger = new WindowsHookEnumerationTrigger();
}

ControllerManager::~ControllerManager()
{
	deleteAllControllers();

	delete mEnumerationTrigger;
	mEnumerationTrigger = NULL;
}

void ControllerManager::update()
{
	bool enumerate = false;
	if ( mEnumerationTrigger->enumerationNeeded() )
		enumerate = true;

	for (DWORD i=0; i<getMaxNumControllers(); i++ )
	{
		if ( mControllers[i]!=NULL || enumerate )
			updateController(i);		
	}
}

void ControllerManager::updateController( DWORD controllerIndex )
{
	assert(controllerIndex<getMaxNumControllers());
	
	DWORD dwResult;    
	XINPUT_STATE state;
	ZeroMemory( &state, sizeof(XINPUT_STATE) );
	dwResult = XInputGetState( controllerIndex, &state );

	if( dwResult==ERROR_SUCCESS )
	{
		// The controller is connected
		Controller* controller = getController( controllerIndex );
		if ( !controller )
		{
			// It wasn't conntected already, we create the object representing it

			// The capabilities of the controller are fetched by the Manager. This gives
			// it a chance to know about the type of controller to create, though right now
			// we systematically create a generic gamepad controller
			XINPUT_CAPABILITIES capabilities;			
			bool ret = getControllerCapabilities( controllerIndex, capabilities );
			assert( ret );
			if ( !ret )
				return;

			controller = addController( controllerIndex, capabilities );
			if ( !controller )
				return;			
		}

		// Update the object that represents it with the current state
		controller->update( state );
	}
	else
	{
		// The controller is not connected 
		Controller* controller = getController( controllerIndex );
		if ( controller )
		{
			// The controller was connected just before, we delete the object representing it
			deleteController( controllerIndex );
		}
	}
}
bool ControllerManager::getControllerCapabilities( DWORD controllerIndex, XINPUT_CAPABILITIES& capabilities )
{ 
	DWORD dwResult;
	ZeroMemory( &capabilities, sizeof(XINPUT_CAPABILITIES) );
	dwResult = XInputGetCapabilities( controllerIndex, XINPUT_FLAG_GAMEPAD, &capabilities );
	if ( dwResult==ERROR_SUCCESS )
		return true;
	return false;
}

Controller*	ControllerManager::addController( DWORD controllerIndex, const XINPUT_CAPABILITIES& capabilities )
{
	assert(controllerIndex<getMaxNumControllers());
	assert(NULL==mControllers[controllerIndex]);

	Controller*	controller = new Controller( controllerIndex, capabilities );
	mControllers[controllerIndex]=controller;

	// Notify
	for ( Listeners::iterator itr=mListeners.begin(); itr!=mListeners.end(); ++itr )
		(*itr)->onControllerConnected( this, controller );

	return controller;
}

void ControllerManager::deleteController( DWORD controllerIndex )
{
	assert(controllerIndex<getMaxNumControllers());
	assert(mControllers[controllerIndex]);

	Controller* controller = mControllers[controllerIndex];
	
	// Notify
	for ( Listeners::iterator itr=mListeners.begin(); itr!=mListeners.end(); ++itr )
		(*itr)->onControllerDisconnecting( this, controller );

	delete controller;
	mControllers[controllerIndex]=NULL;
}

void ControllerManager::deleteAllControllers()
{
	for ( DWORD i=0; i<getMaxNumControllers(); ++i )
	{
		if ( mControllers[i] )
			deleteController(i);
	}
}

void ControllerManager::addListener( Listener* listener )
{
	assert(listener);
	mListeners.push_back(listener);
}

bool ControllerManager::removeListener( Listener* listener )
{
	Listeners::iterator itr = std::find( mListeners.begin(), mListeners.end(), listener );
	if ( itr==mListeners.end() )
		return false;
	mListeners.erase( itr );
	return true;
}

}